/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
/**
 * Class: mxPanningManager
 *
 * Implements a handler for panning.
 */
function mxPanningManager (graph) {
  this.thread = null
  this.active = false
  this.tdx = 0
  this.tdy = 0
  this.t0x = 0
  this.t0y = 0
  this.dx = 0
  this.dy = 0
  this.scrollbars = false
  this.scrollLeft = 0
  this.scrollTop = 0

  this.mouseListener =
	{
	    mouseDown: function (sender, me) { },
	    mouseMove: function (sender, me) { },
	    mouseUp: mxUtils.bind(this, function (sender, me) {
	    	if (this.active) {
	    		this.stop()
	    	}
	    })
	}

  graph.addMouseListener(this.mouseListener)

  this.mouseUpListener = mxUtils.bind(this, function () {
	    	if (this.active) {
	    		this.stop()
	    	}
  })

  // Stops scrolling on every mouseup anywhere in the document
  mxEvent.addListener(document, 'mouseup', this.mouseUpListener)

  var createThread = mxUtils.bind(this, function () {
	    	this.scrollbars = mxUtils.hasScrollbars(graph.container)
	    	this.scrollLeft = graph.container.scrollLeft
	    	this.scrollTop = graph.container.scrollTop

	    	return window.setInterval(mxUtils.bind(this, function () {
      this.tdx -= this.dx
      this.tdy -= this.dy

      if (this.scrollbars) {
        var left = -graph.container.scrollLeft - Math.ceil(this.dx)
        var top = -graph.container.scrollTop - Math.ceil(this.dy)
        graph.panGraph(left, top)
        graph.panDx = this.scrollLeft - graph.container.scrollLeft
        graph.panDy = this.scrollTop - graph.container.scrollTop
        graph.fireEvent(new mxEventObject(mxEvent.PAN))
        // TODO: Implement graph.autoExtend
      } else {
        graph.panGraph(this.getDx(), this.getDy())
      }
    }), this.delay)
  })

  this.isActive = function () {
    return active
  }

  this.getDx = function () {
    return Math.round(this.tdx)
  }

  this.getDy = function () {
    return Math.round(this.tdy)
  }

  this.start = function () {
    this.t0x = graph.view.translate.x
    this.t0y = graph.view.translate.y
    this.active = true
  }

  this.panTo = function (x, y, w, h) {
    if (!this.active) {
      this.start()
    }

    	this.scrollLeft = graph.container.scrollLeft
    	this.scrollTop = graph.container.scrollTop

    w = (w != null) ? w : 0
    h = (h != null) ? h : 0

    var c = graph.container
    this.dx = x + w - c.scrollLeft - c.clientWidth

    if (this.dx < 0 && Math.abs(this.dx) < this.border) {
      this.dx = this.border + this.dx
    } else if (this.handleMouseOut) {
      this.dx = Math.max(this.dx, 0)
    } else {
      this.dx = 0
    }

    if (this.dx == 0) {
      this.dx = x - c.scrollLeft

      if (this.dx > 0 && this.dx < this.border) {
        this.dx = this.dx - this.border
      } else if (this.handleMouseOut) {
        this.dx = Math.min(0, this.dx)
      } else {
        this.dx = 0
      }
    }

    this.dy = y + h - c.scrollTop - c.clientHeight

    if (this.dy < 0 && Math.abs(this.dy) < this.border) {
      this.dy = this.border + this.dy
    } else if (this.handleMouseOut) {
      this.dy = Math.max(this.dy, 0)
    } else {
      this.dy = 0
    }

    if (this.dy == 0) {
      this.dy = y - c.scrollTop

      if (this.dy > 0 && this.dy < this.border) {
        this.dy = this.dy - this.border
      } else if (this.handleMouseOut) {
        this.dy = Math.min(0, this.dy)
      } else {
        this.dy = 0
      }
    }

    if (this.dx != 0 || this.dy != 0) {
      this.dx *= this.damper
      this.dy *= this.damper

      if (this.thread == null) {
        this.thread = createThread()
      }
    } else if (this.thread != null) {
      window.clearInterval(this.thread)
      this.thread = null
    }
  }

  this.stop = function () {
    if (this.active) {
      this.active = false

      if (this.thread != null) {
        window.clearInterval(this.thread)
        this.thread = null
	    	}

      this.tdx = 0
      this.tdy = 0

      if (!this.scrollbars) {
        var px = graph.panDx
        var py = graph.panDy

		    	if (px != 0 || py != 0) {
		    		graph.panGraph(0, 0)
			    	graph.view.setTranslate(this.t0x + px / graph.view.scale, this.t0y + py / graph.view.scale)
		    	}
      } else {
        graph.panDx = 0
        graph.panDy = 0
        graph.fireEvent(new mxEventObject(mxEvent.PAN))
      }
    }
  }

  this.destroy = function () {
    graph.removeMouseListener(this.mouseListener)
    mxEvent.removeListener(document, 'mouseup', this.mouseUpListener)
  }
};

/**
 * Variable: damper
 *
 * Damper value for the panning. Default is 1/6.
 */
mxPanningManager.prototype.damper = 1 / 6

/**
 * Variable: delay
 *
 * Delay in milliseconds for the panning. Default is 10.
 */
mxPanningManager.prototype.delay = 10

/**
 * Variable: handleMouseOut
 *
 * Specifies if mouse events outside of the component should be handled. Default is true.
 */
mxPanningManager.prototype.handleMouseOut = true

/**
 * Variable: border
 *
 * Border to handle automatic panning inside the component. Default is 0 (disabled).
 */
mxPanningManager.prototype.border = 0
